Skip to content

fix: ensure price update follows oracle module after UpdateTokenMetadata in fee abstraction#322

Merged
Thaleszh merged 8 commits into
mainfrom
fix/remove-price-update-feetoken
Apr 9, 2026
Merged

fix: ensure price update follows oracle module after UpdateTokenMetadata in fee abstraction#322
Thaleszh merged 8 commits into
mainfrom
fix/remove-price-update-feetoken

Conversation

@Thaleszh
Copy link
Copy Markdown
Contributor

@Thaleszh Thaleszh commented Apr 8, 2026

Description

  • Ensure price update follows oracle module after UpdateTokenMetadata in fee abstraction
  • Remove price input from updateTokenMetadata, always using next TWAP cycle

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

Updated tests

PR Checklist:

Make sure each step was done:

  • Updated changelog with PR's intent
  • Lint with make lint-fix

@Thaleszh Thaleszh requested a review from jhelison as a code owner April 8, 2026 13:22
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Protobufs updated: MsgUpdateFeeTokens now accepts tokens of new UpdateTokenMetadataCollection (items of UpdateTokenMetadata, no price), and FeeTokenMetadata.enabled field renumbered. Keeper UpdateFeeTokens validates oracle denoms from msg.Tokens.Items, constructs stored fee-token entries with Price zeroed, and persists them. New constructors and validation added for UpdateTokenMetadata and UpdateTokenMetadataCollection (denom/OracleDenom checks, decimals bounds, duplicate-denom detection). Message constructors, tests, README, and CHANGELOG updated accordingly.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: ensuring price updates follow the oracle module after UpdateTokenMetadata in fee abstraction.
Description check ✅ Passed The description is directly related to the changeset, explaining the intent to remove price input and ensure prices follow the oracle module.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/remove-price-update-feetoken

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
x/feeabstraction/types/params.go (1)

138-149: Constructor hardcodes Enabled: true, limiting flexibility.

NewUpdateTokenMetadata always sets Enabled: true, but callers might need to create metadata with Enabled: false (e.g., to disable a fee token via governance). Consider adding an enabled parameter or using struct literal initialization where control is needed.

♻️ Proposed fix to add enabled parameter
 // NewUpdateTokenMetadata creates a new fee token with the given denom and decimals
 func NewUpdateTokenMetadata(
 	denom, oracleDenom string,
 	decimals uint32,
+	enabled bool,
 ) UpdateTokenMetadata {
 	return UpdateTokenMetadata{
 		Denom:       denom,
 		OracleDenom: oracleDenom,
 		Decimals:    decimals,
-		Enabled:     true,
+		Enabled:     enabled,
 	}
 }

Note: This would require updating all call sites to pass the enabled parameter.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/feeabstraction/types/params.go` around lines 138 - 149, The constructor
NewUpdateTokenMetadata currently hardcodes Enabled: true; change its signature
to accept an enabled bool (e.g., NewUpdateTokenMetadata(denom, oracleDenom
string, decimals uint32, enabled bool)) and set UpdateTokenMetadata.Enabled to
that parameter so callers can create disabled tokens, then update all call sites
to pass the appropriate enabled value (or provide an additional convenience
helper if you want a default-true factory). Ensure you update references to
NewUpdateTokenMetadata and the UpdateTokenMetadata struct initialization
accordingly.
x/feeabstraction/keeper/msg_server_test.go (1)

224-230: Consider adding an assertion for the Enabled field.

The test verifies Denom, OracleDenom, Decimals, and Price, but doesn't check that Enabled is correctly persisted. This would help catch the current bug where the Enabled field from the input message is dropped.

♻️ Proposed enhancement
 				for i, token := range tokens.Items {
 					s.Require().Equal(tc.msg.Tokens.Items[i].Denom, token.Denom)
 					s.Require().Equal(tc.msg.Tokens.Items[i].OracleDenom, token.OracleDenom)
 					s.Require().Equal(tc.msg.Tokens.Items[i].Decimals, token.Decimals)
 					s.Require().True(token.Price.IsZero(), "expected price to be 0 for denom %s, got %s", token.Denom, token.Price)
+					s.Require().Equal(tc.msg.Tokens.Items[i].Enabled, token.Enabled, "expected Enabled to match for denom %s", token.Denom)
 				}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/feeabstraction/keeper/msg_server_test.go` around lines 224 - 230, The test
loop in msg_server_test.go that iterates over tokens.Items checks Denom,
OracleDenom, Decimals and Price but omits asserting the Enabled field; add an
assertion inside the same for i, token := range tokens.Items loop comparing
tc.msg.Tokens.Items[i].Enabled with token.Enabled (e.g.,
s.Require().Equal(tc.msg.Tokens.Items[i].Enabled, token.Enabled)) so the test
fails if Enabled is not persisted by the handler.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@x/feeabstraction/keeper/msg_server.go`:
- Around line 113-119: The code in msg_server.go zeroes token prices by calling
types.NewFeeTokenMetadata which hardcodes Enabled=true, dropping the input
message's Enabled field; change the construction of resetItems inside the loop
that processes msg.Tokens.Items so it preserves token.Enabled while only setting
Price to math.LegacyZeroDec() (e.g., create FeeTokenMetadata using a struct
literal or a constructor that accepts an Enabled argument instead of
NewFeeTokenMetadata), ensuring the Denom, OracleDenom, Decimals are copied from
token and Enabled uses token.Enabled before building resetFeeTokens.

In `@x/feeabstraction/types/msg_test.go`:
- Around line 82-88: The test "valid - fee tokens" currently constructs an empty
collection with types.NewUpdateTokenMetadataCollection(), duplicating the "valid
- empty fee tokens" case; replace that empty collection with a non-empty
collection containing one or more valid token metadata entries so the test
actually exercises the non-empty path: build a collection using the project API
(e.g., create one or more types.UpdateTokenMetadata objects and add them to
types.NewUpdateTokenMetadataCollection() or construct the collection with those
entries) and pass it into
types.NewMessageUpdateFeeTokens(authtypes.NewModuleAddress(govtypes.ModuleName).String(),
<non-empty collection>) so the test differs from the empty case.

---

Nitpick comments:
In `@x/feeabstraction/keeper/msg_server_test.go`:
- Around line 224-230: The test loop in msg_server_test.go that iterates over
tokens.Items checks Denom, OracleDenom, Decimals and Price but omits asserting
the Enabled field; add an assertion inside the same for i, token := range
tokens.Items loop comparing tc.msg.Tokens.Items[i].Enabled with token.Enabled
(e.g., s.Require().Equal(tc.msg.Tokens.Items[i].Enabled, token.Enabled)) so the
test fails if Enabled is not persisted by the handler.

In `@x/feeabstraction/types/params.go`:
- Around line 138-149: The constructor NewUpdateTokenMetadata currently
hardcodes Enabled: true; change its signature to accept an enabled bool (e.g.,
NewUpdateTokenMetadata(denom, oracleDenom string, decimals uint32, enabled
bool)) and set UpdateTokenMetadata.Enabled to that parameter so callers can
create disabled tokens, then update all call sites to pass the appropriate
enabled value (or provide an additional convenience helper if you want a
default-true factory). Ensure you update references to NewUpdateTokenMetadata
and the UpdateTokenMetadata struct initialization accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d347aa2c-54ca-4797-9b86-f68d03a6a545

📥 Commits

Reviewing files that changed from the base of the PR and between 16d7ad8 and daf7d1d.

⛔ Files ignored due to path filters (2)
  • x/feeabstraction/types/params.pb.go is excluded by !**/*.pb.go
  • x/feeabstraction/types/tx.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (8)
  • CHANGELOG.md
  • proto/kiichain/feeabstraction/v1beta1/params.proto
  • proto/kiichain/feeabstraction/v1beta1/tx.proto
  • x/feeabstraction/keeper/msg_server.go
  • x/feeabstraction/keeper/msg_server_test.go
  • x/feeabstraction/types/msg.go
  • x/feeabstraction/types/msg_test.go
  • x/feeabstraction/types/params.go

Comment thread x/feeabstraction/keeper/msg_server.go
Comment thread x/feeabstraction/types/msg_test.go
Comment thread x/feeabstraction/keeper/msg_server.go
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 8, 2026

Codecov Report

❌ Patch coverage is 56.75676% with 16 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
x/feeabstraction/types/params.go 64.28% 5 Missing and 5 partials ⚠️
x/feeabstraction/keeper/msg_server.go 0.00% 6 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
x/feeabstraction/README.md (1)

184-184: Clarify TWAP timing to “next BeginBlocker cycle.”

Line 184 could be read as immediate recomputation. Suggest wording like “prices are set to zero so the next BeginBlocker cycle adopts current TWAP values.”

Based on learnings: InitGenesis intentionally resets token prices to zero because TWAP is recomputed in BeginBlocker, and stale carried prices are intentionally avoided.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/feeabstraction/README.md` at line 184, Update the sentence to clarify
timing: replace "Prices are always set to zero so that BeginBlocker adopts the
current TWAP." with wording such as "Prices are set to zero so the next
BeginBlocker cycle adopts current TWAP values." Also mention that InitGenesis
intentionally resets token prices to zero because TWAP is recomputed in
BeginBlocker and this avoids carrying stale prices, referencing InitGenesis,
BeginBlocker, and TWAP to make the intent explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@x/feeabstraction/README.md`:
- Line 184: Update the sentence to clarify timing: replace "Prices are always
set to zero so that BeginBlocker adopts the current TWAP." with wording such as
"Prices are set to zero so the next BeginBlocker cycle adopts current TWAP
values." Also mention that InitGenesis intentionally resets token prices to zero
because TWAP is recomputed in BeginBlocker and this avoids carrying stale
prices, referencing InitGenesis, BeginBlocker, and TWAP to make the intent
explicit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef09a0b1-718c-4fd2-81fe-9fea9f38c462

📥 Commits

Reviewing files that changed from the base of the PR and between cb4e4f5 and 0b9dc81.

📒 Files selected for processing (1)
  • x/feeabstraction/README.md

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
x/feeabstraction/keeper/msg_server.go (1)

116-123: ⚠️ Potential issue | 🟠 Major

Preserve token.Enabled when rebuilding the zero-price collection.

This rewrite intentionally zeroes Price, but it also drops the Enabled value from UpdateTokenMetadata. As written, governance cannot reliably persist a token's enabled/disabled state through UpdateFeeTokens.

🐛 Proposed fix
 	resetItems := make([]types.FeeTokenMetadata, len(msg.Tokens.Items))
 	for i, token := range msg.Tokens.Items {
-		resetItems[i] = types.NewFeeTokenMetadata(token.Denom, token.OracleDenom, token.Decimals, math.LegacyZeroDec())
+		resetItems[i] = types.FeeTokenMetadata{
+			Denom:       token.Denom,
+			OracleDenom: token.OracleDenom,
+			Decimals:    token.Decimals,
+			Price:       math.LegacyZeroDec(),
+			Enabled:     token.Enabled,
+		}
 	}
 	resetFeeTokens := types.NewFeeTokenMetadataCollection(resetItems...)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/feeabstraction/keeper/msg_server.go` around lines 116 - 123, When
rebuilding resetItems from msg.Tokens.Items you currently drop the Enabled flag;
preserve each token's Enabled state by carrying token.Enabled into the new
metadata before calling ms.FeeTokens.Set. Specifically, in the loop that builds
resetItems (iterating msg.Tokens.Items) create the zero-priced metadata via
types.NewFeeTokenMetadata(...) and then set the Enabled field from the original
token (e.g. m := types.NewFeeTokenMetadata(...); m.Enabled = token.Enabled;
resetItems[i] = m), or use an available constructor that accepts Enabled, so
types.FeeTokenMetadata entries keep their Enabled value when you create
resetFeeTokens and call ms.FeeTokens.Set.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@x/feeabstraction/keeper/msg_server.go`:
- Around line 116-123: When rebuilding resetItems from msg.Tokens.Items you
currently drop the Enabled flag; preserve each token's Enabled state by carrying
token.Enabled into the new metadata before calling ms.FeeTokens.Set.
Specifically, in the loop that builds resetItems (iterating msg.Tokens.Items)
create the zero-priced metadata via types.NewFeeTokenMetadata(...) and then set
the Enabled field from the original token (e.g. m :=
types.NewFeeTokenMetadata(...); m.Enabled = token.Enabled; resetItems[i] = m),
or use an available constructor that accepts Enabled, so types.FeeTokenMetadata
entries keep their Enabled value when you create resetFeeTokens and call
ms.FeeTokens.Set.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f9d91311-847e-4e2f-a3ae-65284ee3d240

📥 Commits

Reviewing files that changed from the base of the PR and between 0b9dc81 and 0477682.

📒 Files selected for processing (1)
  • x/feeabstraction/keeper/msg_server.go

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
CHANGELOG.md (1)

17-23: Clarify lifecycle wording and use canonical type names in changelog entries.

These bullets are directionally correct, but the phrasing can be more precise: prices are reset to zero during metadata update and then populated by the oracle/TWAP cycle; also prefer UpdateFeeTokens / UpdateTokenMetadata naming for consistency.

✍️ Suggested wording
-- Ensure feeTokenMetadata initial prices after updateFeeTokenMetadata is picked up from oracle
+- Ensure fee token metadata prices are reset on `UpdateFeeTokens` and populated by the next oracle/TWAP cycle

 ### Removed

-- Removed price field input in updateTokenMetadata request
+- Remove `price` field input from `UpdateTokenMetadata` request

Based on learnings: InitGenesis and fee abstraction intentionally start prices at zero so oracle TWAP recomputes authoritative prices.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 17 - 23, Update the changelog bullets to use the
canonical names UpdateFeeTokens and UpdateTokenMetadata, clarify that fee token
prices are reset to zero during metadata updates (and then populated by the
oracle/TWAP cycle), and explicitly mention that InitGenesis and the fee
abstraction intentionally start prices at zero so TWAP recomputes authoritative
prices; keep the other entries referencing DecCoins.Validate in
RewardPool.ValidateGenesis and denom consistency in GenesisState.Validate with
Params.TokenDenom but rephrase to use the canonical type/function names and
precise lifecycle wording accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@CHANGELOG.md`:
- Around line 17-23: Update the changelog bullets to use the canonical names
UpdateFeeTokens and UpdateTokenMetadata, clarify that fee token prices are reset
to zero during metadata updates (and then populated by the oracle/TWAP cycle),
and explicitly mention that InitGenesis and the fee abstraction intentionally
start prices at zero so TWAP recomputes authoritative prices; keep the other
entries referencing DecCoins.Validate in RewardPool.ValidateGenesis and denom
consistency in GenesisState.Validate with Params.TokenDenom but rephrase to use
the canonical type/function names and precise lifecycle wording accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 68ca2106-a83a-4d05-89c3-36098e0c6262

📥 Commits

Reviewing files that changed from the base of the PR and between 0477682 and f849172.

📒 Files selected for processing (1)
  • CHANGELOG.md

@Thaleszh Thaleszh merged commit 84c20e4 into main Apr 9, 2026
10 checks passed
@Thaleszh Thaleszh deleted the fix/remove-price-update-feetoken branch April 9, 2026 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants